//
#import "BKType.h"
#import <objc/runtime.h>
#import "avcall.h"

//#define _C_ID       '@'
//#define _C_CLASS    '#'
//#define _C_SEL      ':'
//#define _C_CHR      'c'
//#define _C_UCHR     'C'
//#define _C_SHT      's'
//#define _C_USHT     'S'
//#define _C_INT      'i'
//#define _C_UINT     'I'
//#define _C_LNG      'l'
//#define _C_ULNG     'L'
//#define _C_LNG_LNG  'q'
//#define _C_ULNG_LNG 'Q'
//#define _C_FLT      'f'
//#define _C_DBL      'd'
//#define _C_BFLD     'b'
//#define _C_BOOL     'B'
//#define _C_VOID     'v'
//#define _C_UNDEF    '?'
//#define _C_PTR      '^'
//#define _C_CHARPTR  '*'
//#define _C_ATOM     '%'
//#define _C_ARY_B    '['
//#define _C_ARY_E    ']'
//#define _C_UNION_B  '('
//#define _C_UNION_E  ')'
//#define _C_STRUCT_B '{'
//#define _C_STRUCT_E '}'
//#define _C_VECTOR   '!'
//#define _C_CONST    'r'

#define ALIST *(av_alist*)alist

@interface BKPrimitiveType : BKType
{
	char _type;
}
@end

@implementation BKPrimitiveType

- (id) initWithTypeChar: (char) type
{
	if(self = [super init])
	{
		_type = type;
		switch(type)
		{
			case _C_ID:
			case _C_CLASS:
			case _C_SEL:
			case _C_CHR:
			case _C_UCHR:
			case _C_SHT:
			case _C_USHT:
			case _C_INT:
			case _C_UINT:
			case _C_LNG:
			case _C_ULNG:
			case _C_LNG_LNG:
			case _C_ULNG_LNG:
			case _C_FLT:
			case _C_DBL:
			case _C_BFLD:
			case _C_BOOL:
			case _C_VOID:
			case _C_UNDEF:
				break;
			default:
				[self dealloc];
				return nil;
		}
	}
	return self;
}

- (size_t) byteCount
{
	switch(_type)
	{
		case _C_ID:
			return sizeof(id);
		case _C_CLASS:
			return sizeof(Class*);
		case _C_SEL:
			return sizeof(SEL);
		case _C_CHR:
			return sizeof(char);
		case _C_UCHR:
			return sizeof(unsigned char);
		case _C_SHT:
			return sizeof(short);
		case _C_USHT:
			return sizeof(unsigned short);
		case _C_INT:
			return sizeof(int);
		case _C_UINT:
			return sizeof(unsigned int);
		case _C_LNG:
			return sizeof(long);
		case _C_ULNG:
			return sizeof(unsigned long);
		case _C_LNG_LNG:
			return sizeof(long long);
		case _C_ULNG_LNG:
			return sizeof(unsigned long long);
		case _C_FLT:
			return sizeof(float);
		case _C_DBL:
			return sizeof(double);
		case _C_BFLD:
			return sizeof(unsigned);
		case _C_BOOL:
			return sizeof(BOOL);
		case _C_VOID:
			return 0;
		case _C_UNDEF:
			return 0;
	}
	return 0;
}

- (BOOL) startFunction: (VoidFunc) func returnAddress: (void*) retAddr toCall: (void*) alist
{
	switch(_type)
	{
		case _C_ID:
			av_start_ptr(ALIST, func, void, retAddr);
			return YES;
			
		case _C_CLASS:
			av_start_ptr(ALIST, func, Class, retAddr);
			return YES;
			
		case _C_SEL:
			av_start_ptr(ALIST, func, void/*SEL*/, retAddr);
			return YES;
			
		case _C_CHR:
			av_start_char(ALIST, func, retAddr);
			return YES;
			
		case _C_UCHR:
			av_start_uchar(ALIST, func, retAddr);
			return YES;
			
		case _C_SHT:
			av_start_short(ALIST, func, retAddr);
			return YES;
			
		case _C_USHT:
			av_start_ushort(ALIST, func, retAddr);
			return YES;
			
		case _C_INT:
			av_start_int(ALIST, func, retAddr);
			return YES;
			
		case _C_UINT:
			av_start_uint(ALIST, func, retAddr);
			return YES;
			
		case _C_LNG:
			av_start_long(ALIST, func, retAddr);
			return YES;
			
		case _C_ULNG:
			av_start_ulong(ALIST, func, retAddr);
			return YES;
			
		case _C_LNG_LNG:
			av_start_longlong(ALIST, func, retAddr);
			return YES;
			
		case _C_ULNG_LNG:
			av_start_ulonglong(ALIST, func, retAddr);
			return YES;
			
		case _C_FLT:
			av_start_float(ALIST, func, retAddr);
			return YES;
			
		case _C_DBL:
			av_start_double(ALIST, func, retAddr);
			return YES;
			
		case _C_BFLD:
			av_start_uint(ALIST, func, retAddr);
			return YES;
			
		case _C_BOOL:
			av_start_int(ALIST, func, retAddr);
			return YES;
			
		case _C_VOID:
		case _C_UNDEF:
			av_start_void(ALIST, func);
			return YES;
	}
	return NO;
}

- (BOOL) addParam: (id) value toCall: (void*) alist
{
	if(![value isKindOfClass: [NSValue class]])
		return NO;
	
	BOOL isNumber = [value isKindOfClass: [NSNumber class]];
	
	switch(_type)
	{
		case _C_ID:
		{
			id obj = [value nonretainedObjectValue];
			av_ptr(ALIST, id, obj);
			return YES;
		}
		case _C_CLASS:
		{
			Class* klass = [value pointerValue];
			av_ptr(ALIST, Class*, klass);
			return YES;
		}
		case _C_SEL:
		{
			SEL sel = [value pointerValue];
			av_ptr(ALIST, SEL, sel);
			return YES;
		}
		case _C_CHR:
			if(isNumber)
			{
				char val = [value charValue];
				av_char(ALIST, val);
				return YES;
			}
			return NO;
		
		case _C_UCHR:
			if(isNumber)
			{
				unsigned char val = [value unsignedCharValue];
				av_uchar(ALIST, val);
				return YES;
			}
			return NO;
		
		case _C_SHT:
			if(isNumber)
			{
				short val = [value shortValue];
				av_short(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_USHT:
			if(isNumber)
			{
				unsigned short val = [value unsignedShortValue];
				av_ushort(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_INT:
			if(isNumber)
			{
				int val = [value intValue];
				av_int(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_UINT:
			if(isNumber)
			{
				unsigned int val = [value unsignedIntValue];
				av_uint(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_LNG:
			if(isNumber)
			{
				long val = [value longValue];
				av_long(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_ULNG:
			if(isNumber)
			{
				unsigned long val = [value unsignedLongValue];
				av_ulong(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_LNG_LNG:
			if(isNumber)
			{
				long long val = [value longLongValue];
				av_longlong(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_ULNG_LNG:
			if(isNumber)
			{
				unsigned long long val = [value unsignedLongLongValue];
				av_ulonglong(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_FLT:
			if(isNumber)
			{
				float val = [value floatValue];
				av_float(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_DBL:
			if(isNumber)
			{
				double val = [value doubleValue];
				av_double(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_BFLD:
			if(isNumber)
			{
				unsigned val = [value unsignedIntValue];
				av_uint(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_BOOL:
			if(isNumber)
			{
				BOOL val = [value boolValue];
				av_int(ALIST, val);
				return YES;
			}
			return NO;
			
		case _C_VOID:
			return NO;
		case _C_UNDEF:
			return NO;
	}
	return NO;
}

- (NSString*) description
{
	switch(_type)
	{
		case _C_ID:
			return @"id";
		case _C_CLASS:
			return @"Class*";
		case _C_SEL:
			return @"SEL";
		case _C_CHR:
			return @"char";
		case _C_UCHR:
			return @"unsigned char";
		case _C_SHT:
			return @"short";
		case _C_USHT:
			return @"unsigned short";
		case _C_INT:
			return @"int";
		case _C_UINT:
			return @"unsigned int";
		case _C_LNG:
			return @"long";
		case _C_ULNG:
			return @"unsigned long";
		case _C_LNG_LNG:
			return @"long long";
		case _C_ULNG_LNG:
			return @"unsigned long long";
		case _C_FLT:
			return @"float";
		case _C_DBL:
			return @"double";
		case _C_BFLD:
			return @"unsigned";
		case _C_BOOL:
			return @"BOOL";
		case _C_VOID:
			return @"void";
		case _C_UNDEF:
			return @"void /*undef*/";
	}
	return [NSString stringWithCString: &_type length: 1];
}

@end


@interface BKPointerType : BKType
{
	BKType* _type;
	BOOL _const;
}
@end

@implementation BKPointerType

- (id) initWithBKType: (BKType*) type constant: (BOOL) constant
{
	if(self = [super init])
	{
		if(type == nil)
		{
			[self dealloc];
			return nil;
		}
		_type = [type retain];
		_const = constant;
	}
	return self;
}

- (void) dealloc
{
	[_type release];
	[super dealloc];
}

- (size_t) byteCount
{
	return sizeof(void*);
}

- (BKType*) removePlaceholders: (NSDictionary*) dict
{
	_type = [[[_type autorelease] removePlaceholders: dict] retain];
	return self;
}

- (BOOL) startFunction: (VoidFunc) func returnAddress: (void*) retAddr toCall: (void*) alist
{
	av_start_ptr(ALIST, func, void, retAddr);
	return YES;
}

- (BOOL) addParam: (id) value toCall: (void*) alist
{
	if(![value isKindOfClass: [NSValue class]])
		return NO;
	
	void* obj = [value pointerValue];
	av_ptr(ALIST, void*, obj);
	return YES;
}

- (NSString*) description
{
	return [[_type description] stringByAppendingString: @" *"];
}

@end


@interface BKStructType : BKType
{
	NSString* _name;
	NSArray* _memberNames;
	NSArray* _memberTypes;
}
@end

@implementation BKStructType

- (id) initWithName: (NSString*) name memberNames: (NSArray*) memberNames memberTypes: (NSArray*) memberTypes
{
	if(self = [super init])
	{
		_name = [name copy];
		_memberNames = [memberNames retain];
		_memberTypes = [memberTypes retain];
	}
	return self;
}

- (void) dealloc
{
	[_name release];
	[_memberNames release];
	[_memberTypes release];
	[super dealloc];
}

- (size_t) byteCount
{
	size_t theSize = 0;
	id member;
	for(member in _memberTypes)
		theSize += [member byteCount];
	return theSize;
}

- (BKType*) removePlaceholders: (NSDictionary*) dict
{
	NSMutableArray* newTypes = [NSMutableArray array];
	id member;
	for(member in _memberTypes)
	{
		member = [member removePlaceholders: dict];
		[newTypes addObject: member];
	}
	[_memberTypes autorelease];
	_memberTypes = [newTypes retain];
	return self;
}

#define STRUCT(x) struct struct##x {char c[x];}

STRUCT( 1);STRUCT( 2);STRUCT( 3);STRUCT( 4);STRUCT( 5);STRUCT( 6);STRUCT( 7);STRUCT( 8);STRUCT( 9);STRUCT(10);
STRUCT(11);STRUCT(12);STRUCT(13);STRUCT(14);STRUCT(15);STRUCT(16);STRUCT(17);STRUCT(18);STRUCT(19);STRUCT(20);

#define CASE(x) case x: av_struct(ALIST, struct struct##x, *(struct struct##x*)val); break;

- (BOOL) addParam: (id) value toCall: (void*) alist
{
	BKType* valueType = [BKType typeWithString: [NSString stringWithCString: [value objCType]]];
	size_t theSize = [self byteCount], valueSize = [valueType byteCount];
	if(theSize != valueSize)
		return NO;
	
	char buffer[256];
	void* val = (theSize <= 256 ? buffer : malloc(theSize));
	
	[value getValue: val];
	
	switch(theSize)
	{
		CASE( 1);CASE( 2);CASE( 3);CASE( 4);CASE( 5);CASE( 6);CASE( 7);CASE( 8);CASE( 9);CASE(10);
		CASE(11);CASE(12);CASE(13);CASE(14);CASE(15);CASE(16);CASE(17);CASE(18);CASE(19);CASE(20);
		default:
			av_struct(ALIST, struct struct1, *(struct struct1*)val);
			break;
	}
	
	if(val != buffer)
		free(val);
	return YES;
}

- (NSString*) description
{
	NSMutableString* str = [NSMutableString string];
	[str appendFormat: @"struct %@ { ", _name];
	
	NSInteger i;
	for(i = 0; i < [_memberTypes count]; i++)
		[str appendFormat: @"%@ %@; ", [_memberTypes objectAtIndex: i], [_memberNames objectAtIndex: i]];
	
	[str appendString: @"}"];
	return str;
}

@end


@interface BKStructPlaceholderType : BKType
{
	NSString* _name;
}
@end

@implementation BKStructPlaceholderType

- (id) initWithName: (NSString*) name
{
	if(self = [super init])
	{
		_name = [name copy];
	}
	return self;
}

- (void) dealloc
{
	[_name release];
	[super dealloc];
}

- (BKType*) removePlaceholders: (NSDictionary*) dict
{
	id theType = [dict objectForKey: _name];
	return (theType ? theType : self);
}

- (NSString*) description
{
	return [NSString stringWithFormat: @"struct %@", _name];
}

@end


@implementation BKType

+ (id) structDict
{
	static NSMutableDictionary* dict = nil;
	if(dict == nil)
		dict = [[NSMutableDictionary alloc] init];
	return dict;
}

+ (id) typeWithString: (NSString*) theType range: (NSRange*) range
{
	NSString* type = [theType substringWithRange: *range];
	
	if(type == nil)
		return nil;
	
	NSInteger len = [type length];
	if(len == 0)
		return nil;
	
	BOOL constant = NO;
	NSInteger i = 0;
	
	unichar c = [type characterAtIndex: i];
	if(c == 'r')
	{
		constant = YES;
		if(i >= len - 1)
			return nil;
		c = [type characterAtIndex: ++i];
	}
	
	switch(c)
	{
		case _C_ID:
		case _C_CLASS:
		case _C_SEL:
		case _C_CHR:
		case _C_UCHR:
		case _C_SHT:
		case _C_USHT:
		case _C_INT:
		case _C_UINT:
		case _C_LNG:
		case _C_ULNG:
		case _C_LNG_LNG:
		case _C_ULNG_LNG:
		case _C_FLT:
		case _C_DBL:
		case _C_BFLD:
		case _C_BOOL:
		case _C_VOID:
		case _C_UNDEF:
			range->length = i + 1;
			return [[[BKPrimitiveType alloc] initWithTypeChar: c] autorelease];
			
		case _C_CHARPTR:
			range->length = i + 1;
			return [[[BKPointerType alloc]
				initWithBKType: [[[BKPrimitiveType alloc] initWithTypeChar: _C_CHR] autorelease]
				constant: constant] autorelease];
		
		case _C_PTR:
		{
			NSRange typeRange = NSMakeRange(i + 1, len - i - 1);
			BKType* ptr = [BKType typeWithString: type range: &typeRange];
			if(ptr == nil)
				return nil;
			range->length = i + 1 + typeRange.length;
			return [[[BKPointerType alloc] initWithBKType: ptr constant: constant] autorelease];
		}
		case _C_STRUCT_B:
		{
			i++;
			NSRange r = [type rangeOfString: @"="];
			if((r.length == 1) && (r.length < len))
			{
				NSString* name = [type substringWithRange: NSMakeRange(i, r.location - i)];
				NSMutableArray* theNames = [NSMutableArray array];
				NSMutableArray* theTypes = [NSMutableArray array];
				i = r.location + r.length;
				
				while([type characterAtIndex: i] != _C_STRUCT_E)
				{
					NSString* memberName = @"";
					if((c = [type characterAtIndex: i]) == '"')
					{
						NSMutableString* str = [NSMutableString string];
						while((c = [type characterAtIndex: ++i]) != '"')
							[str appendString: [NSString stringWithCharacters: &c length: 1]];
						i++;
						memberName = str;
					}
					
					NSRange memberRange = NSMakeRange(i, len - i);
					BKType* memberType = [BKType typeWithString: type range: &memberRange];
					if(memberType == nil)
						return nil;
					
					[theNames addObject: memberName];
					[theTypes addObject: memberType];
					i += memberRange.length;
				}
				range->length = i + 1;
				
				NSMutableDictionary* structDict = [self structDict];
				id theStruct = [structDict objectForKey: name];
				if(theStruct == nil)
				{
					theStruct = [[[BKStructType alloc]
						initWithName: name memberNames: theNames memberTypes: theTypes] autorelease];
					[structDict setObject: theStruct forKey: name];
				}
				return theStruct;
			}
			else
			{
				NSMutableString* name = [NSMutableString string];
				while((c = [type characterAtIndex: i]) != _C_STRUCT_E)
				{
					[name appendString: [NSString stringWithCharacters: &c length: 1]];
					i++;
				}
				range->length = i + 1;
				
				NSMutableDictionary* structDict = [self structDict];
				id theStruct = [structDict objectForKey: name];
				if(theStruct == nil)
					theStruct = [[[BKStructPlaceholderType alloc] initWithName: name] autorelease];
				return theStruct;
			}
		}
		case _C_STRUCT_E:
		case _C_ARY_B:
		case _C_ARY_E:
		case _C_UNION_B:
		case _C_UNION_E:
			return nil; // FIXME
	}
	return nil;
}

+ (id) typeWithString: (NSString*) type
{
	NSRange r = NSMakeRange(0, [type length]);
	return [self typeWithString: type range: &r];
}

- (size_t) byteCount
{
	return 0;
}

- (BKType*) removePlaceholders: (NSDictionary*) dict
{
	return self;
}

- (BKType*) removePlaceholders
{
	return [self removePlaceholders: [BKType structDict]];
}

- (BOOL) startFunction: (VoidFunc) func returnAddress: (void*) retAddr toCall: (void*) alist
{
	return NO;
}

- (BOOL) addParam: (id) value toCall: (void*) alist
{
	return NO;
}

@end
